package com.ruoyi.common.utils.uuid;

import com.ruoyi.common.config.RuoYiConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.util.Objects;

/**
 * @Author: lzp
 * @description: 短id工具类
 * 注意： 使用此工具类，需要自行配置定时任务每日删除该缓存key，具体策略看生成方法的注解。
 * 参考博文：java设计实现10位纯数字短id工具类【从浅入深，保姆级】(https://blog.csdn.net/qq_42365534/article/details/126454795)
 * @Date: 2022/8/15
 */
@Component
@Slf4j
public class ShortIdUtils {

    /**
     * 短id 缓存key
     */
    public static final String SHORT_ID_SET_KEY = "short_id_set";

    /**
     * 最大重试次数
     */
    public static final int MAX_RETRY_COUNT = 100;

    private static SetOperations<String, Object> setOperations;
    private static int userIdLength;
    private static int orderIdLength;

    @Autowired
    public void setSetOperations(SetOperations<String, Object> setOperations, RuoYiConfig ruoYiConfig) {
        ShortIdUtils.setOperations = setOperations;
        ShortIdUtils.userIdLength = ruoYiConfig.getUserIdLength();
        ShortIdUtils.orderIdLength = ruoYiConfig.getOrderIdLength();
    }

    /**
     * 生成用户id。
     *
     * @return
     */
    public static Long generateUserId() {
        return generate(userIdLength);
    }

    /**
     * 生成Long类型的订单id。
     *
     * @return
     */
    public static Long generateOrderId() {
        return generate(orderIdLength);
    }

    /**
     * 生成String类型的订单id。
     *
     * @return
     */
    public static String generateOrderIdStr() {
        return String.valueOf(generate(orderIdLength));
    }

    /**
     * 生成规则 年份后两位【2位】 + 1年内第几天【3位】 + 随机数【n位】
     * 理论上来说，一百年内不会重复
     * 注意：1.前五位保证每天生成的id不重复，后五位会在redis缓存 set集合中进行碰撞校验，
     * 2.请保证每天0时清除缓存，保证缓存中的短id数量较少提高性能，减少内存浪费
     */
    public static Long generate(int length) {
        Long id = null;
        int count = 0;
        boolean exists = true;
        String idStr;
        // 如果redis中已有该id，重试生成短id
        while (exists) {
            id = generateShortId(length);
            idStr = id.toString();
            //setOperations.add: 往redis set中添加元素，key不存在时，自动新建。该方法返回被添加的元素个数。如果欲添加的所有value都重复则返回0。
            exists = Objects.requireNonNull(setOperations.add(SHORT_ID_SET_KEY + "_" + idStr.substring(0, 5), idStr)).intValue() == 0;
            count++;
            if (count >= MAX_RETRY_COUNT) {
                log.error("尝试生成短id发生碰撞超过10次！！");
                return null;
            }
        }
        return id;
    }

    /**
     * 生成短id
     */
    private static Long generateShortId(int length) {
        LocalDate now = LocalDate.now();
        String yearStr = now.getYear() + "";
        yearStr = yearStr.substring(2);
        String dayOfYearStr = now.getDayOfYear() + "";
        if (dayOfYearStr.length() < 3) {
            StringBuilder zeroStr = new StringBuilder();
            for (int i = dayOfYearStr.length(); i < 3; i++) {
                zeroStr.append("0");
            }
            dayOfYearStr = zeroStr + dayOfYearStr;
        }
        StringBuilder id = new StringBuilder(yearStr + dayOfYearStr);
        for (int i = id.length(); i < length; i++) {
            id.append(getRandomIndex(0, 9));
        }
        return Long.parseLong(id.toString());
    }


    /**
     * 返回范围内的随机int
     *
     * @param min 最小值
     * @param max 最大值
     * @return
     */
    private static int getRandomIndex(int min, int max) {
        return min + (int) (Math.random() * (max - min + 1));
    }

}

